<html>
  <head>
    <title>
      选择状态结构
    </title>
  </head>
  <script>
    // 选择状态结构
            /*
                构建良好的状态结构可以让组件变得易于修改和调试，而不会经常出错。
                以下是你在构建状态时应该考虑的一些建议：
            */


            //构建状态的原则
                    /*
                        当你编写一个存有状态的组件时，你需要选择使用"多少个状态变量"以及它们都是"怎样的数据格式"。
                        尽管选择次优的状态结构下也可以编写出正确的程序，但有几个原则可以知道您做出更好的决策：

                              1、合并关联的状态(state):
                                      如果你总是同时更新两个或者更多的状态变量，请考虑将它们合并为一个单独的状态变量。

                              2、避免互相矛盾的状态(state):
                                      当状态结构中存在多个互相矛盾或者"不一致"的状态时，你就可能为此留下隐患。应该避免这种情况。

                              3、避免荣誉的状态(state):
                                      如果你能在渲染起见从组件的属性(props)或现有的状态变量中计算出一些信息，则不应该将这些信息放入该组件的状态中。

                              4、避免重复的状态(state):
                                      当同一个数据在多个状态之间或在多个嵌套对象中重复时，这会很难保持它们的同步。应该尽力减少重复。

                              5、避免深度嵌套的状态(state):
                                      深度分层的状态更新起来不是很方便。如果可能的化，尽可能以扁平化的方式构建状态。

                        这些原则背后的目标是 "让state易于更新而不引入错误"。
                        从状态中删除冗余和重复数据有助于确保“所有部分保持同步”。这类似于数据库工程师想要"规范化"数据库结构，以减少出现错误的机会。

                        用爱因斯坦的话说　＂让你的状态尽可能简单，但不要过于简单＂
                                
                    */

                    //一、 合并关联的状态(state)
                              /*
                                有时候，你可能会不确定是使用单个状态变量还是多个状态变量。
                              */
                                   // 你会像下面这样做吗：
                                        const [x,setX] = useState(0);
                                        const [y,setY] = useState(0);
                                   // 或者这样？
                                        const [position,setPosition] =useState({x:0,y:0});

                              /*
                                  从技术上讲，你可以使用其中任何一种方法。
                                  但是有个原则:"如果某两个 state 变量总是一起变化，则将它们统一成一个 state 变量可能更好"
                              */

                                      import { useState } from 'react';

                                      export default function MovingDot() {
                                        const [position, setPosition] = useState({
                                          x: 0,
                                          y: 0
                                        });
                                        return (
                                          <div
                                            onPointerMove={e => {
                                              setPosition({
                                                x: e.clientX,
                                                y: e.clientY
                                              });
                                            }}
                                            style={{
                                              position: 'relative',
                                              width: '100vw',
                                              height: '100vh',
                                            }}>
                                            <div style={{
                                              position: 'absolute',
                                              backgroundColor: 'red',
                                              borderRadius: '50%',
                                              transform: `translate(${position.x}px, ${position.y}px)`,
                                              left: -10,
                                              top: -10,
                                              width: 20,
                                              height: 20,
                                            }} />
                                          </div>
                                        )
                                      }

                              /*
                                     另一种情况是，你将数据整合到一个对象或一个数组中时，你不知道需要多少个 state 片段。
                                     例如，当你有一个用户可以添加自定义字段的表单时，这将会很有帮助。 
                              */

                              /*
                                      如果你的 state 变量是一个对象时，请记住，你不能只更新其中的一个字段 而不显式复制其他字段。
                                      例如，在上面的例子中，你不能写成 setPosition({ x: 100 })，因为它根本就没有 y 属性! 相反，如果你想要仅设置 x，则可执行 setPosition({ ...position, x: 100 })，或将它们分成两个 state 变量，并执行 setX(100)。
                              */


                    // 二、避免矛盾的状态
                              //  下面是带有 isSending 和 isSent 两个 state 变量的酒店反馈表单：
                                      import { useState } from 'react';

                                      export default function FeedbackForm() {
                                            const [text, setText] = useState('');
                                            const [isSending, setIsSending] = useState(false);
                                            const [isSent, setIsSent] = useState(false);

                                            async function handleSubmit(e) {
                                              e.preventDefault();
                                              setIsSending(true);
                                              await sendMessage(text);
                                              setIsSending(false);
                                              setIsSent(true);
                                            }

                                            if (isSent) {
                                              return <h1>Thanks for feedback!</h1>
                                            }

                                            return (
                                              <form onSubmit={handleSubmit}>
                                                <p>How was your stay at The Prancing Pony?</p>
                                                <textarea
                                                  disabled={isSending}
                                                  value={text}
                                                  onChange={e => setText(e.target.value)}
                                                />
                                                <br />
                                                <button
                                                  disabled={isSending}
                                                  type="submit"
                                                >
                                                  Send
                                                </button>
                                                {isSending && <p>Sending...</p>}
                                              </form>
                                            );
                                      }

                                      // 假装发送一条消息。
                                      function sendMessage(text) {
                                        return new Promise(resolve => {
                                          setTimeout(resolve, 2000);
                                        });
                                      }

                                /*
                                     尽管这段代码是有效的，但也会让一些状态"极其难处理"。
                                     例如，如果你忘记同时调用 setIsSent 和 setIsSending，则可能会出现 isSending 和 isSent 同时为 true 的情况。
                                     你的组件越复杂，你就越难理解发生了什么。
                                */
                                      /*
                                          因为 isSending 和 isSent 不应同时为 true，
                                          所以最好用一个 status 变量来代替它们，
                                          这个 state 变量可以采取三种有效状态其中之一：'typing' (初始), 'sending', 和 'sent':
                                      */ 


                  // 三、避免冗余的状态
                          /*
                              如果你能在渲染期间从组件的props或其现有的状态变量中计算出一些信息，则不应该吧这些信息放到该组件的状态中。
                          */
                                    // 例如，以这个表单为例。它可以运行，但你能找到其中任何冗余的 state 吗？
                                            import { useState } from 'react';

                                            export default function Form() {
                                                  const [firstName, setFirstName] = useState('');
                                                  const [lastName, setLastName] = useState('');
                                                  const [fullName, setFullName] = useState('');

                                                  function handleFirstNameChange(e) {
                                                    setFirstName(e.target.value);
                                                    setFullName(e.target.value + ' ' + lastName);
                                                  }

                                                  function handleLastNameChange(e) {
                                                    setLastName(e.target.value);
                                                    setFullName(firstName + ' ' + e.target.value);
                                                  }

                                                  return (
                                                    <>
                                                      <h2>Let’s check you in</h2>
                                                      <label>
                                                        First name:{' '}
                                                        <input
                                                          value={firstName}
                                                          onChange={handleFirstNameChange}
                                                        />
                                                      </label>
                                                      <label>
                                                        Last name:{' '}
                                                        <input
                                                          value={lastName}
                                                          onChange={handleLastNameChange}
                                                        />
                                                      </label>
                                                      <p>
                                                        Your ticket will be issued to: <b>{fullName}</b>
                                                      </p>
                                                    </>
                                                  );
                                            }

                                      /*
                                           这个表单有三个 state 变量：firstName、lastName 和 fullName。然而，fullName 是多余的。
                                           在渲染期间，你始终可以从 firstName 和 lastName 中计算出 fullName，因此需要把它从 state 中删除。 
                                      */

                  // 四、避免重复的状态
                          //    下面这个菜单列表组件可以让你在多种旅行小吃中选择一个：
                                      import { useState } from 'react';

                                      const initialItems = [
                                        { title: 'pretzels', id: 0 },
                                        { title: 'crispy seaweed', id: 1 },
                                        { title: 'granola bar', id: 2 },
                                      ];

                                      export default function Menu() {
                                        const [items, setItems] = useState(initialItems);
                                        const [selectedItem, setSelectedItem] = useState(
                                          items[0]
                                        );

                                        return (
                                          <>
                                            <h2>What's your travel snack?</h2>
                                            <ul>
                                              {items.map(item => (
                                                <li key={item.id}>
                                                  {item.title}
                                                  {' '}
                                                  <button onClick={() => {
                                                    setSelectedItem(item);
                                                  }}>Choose</button>
                                                </li>
                                              ))}
                                            </ul>
                                            <p>You picked {selectedItem.title}.</p>
                                          </>
                                        );
                                      }

                                  /*
                                      当前，它将所选元素作为对象存储自爱 selectedItem 状态变量中。
                                      然而，这并不好： selectedItem的内容与 items 列表中的某个项是"同一个对象"。
                                      这意味着关于该项本身的信息在两个地方产生了重复。
                                  */

                    
  </script>
</html>